﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Resources;
using System.Reflection;
using System.Globalization;
using System.Xml;
using System.IO;
using ProgNetDxComponents.ExtenderProviders.Browsers;
using System.Data;

namespace ProgNetDxComponents.ExtenderProviders.Language
{
    public class ProgNetLanguage
    {
        private static string LngBrowserCode;
        public static void RegisterLanguageBrowser(string browserName, string browserCaption, string codeColumnCaption, string nameColumnCaption)
        {
            if (BrowserCache.GetBrowser(browserName, false) == null)
            {
                LngBrowserCode = browserName;
                Browser b = new Browser();
                b.Static = true;
                b.GetDataOnce = true;
                b.Name = browserName;
                b.Caption = browserCaption;
                BrowserColumn col = new BrowserColumn();
                col.Name = "CODE";
                col.Caption = codeColumnCaption;
                b.Columns.Add(col);

                col = new BrowserColumn();
                col.Name = "NAME";
                col.Caption = nameColumnCaption;
                b.Columns.Add(col);
                b.DisplayMember = "NAME";
                b.ValueMember = "CODE";
                b.DisplayType = BrowserDisplayType.ComboLookUp;

                BrowserCache.GetStaticData += new EventHandler<GetBrowserDataEventArgs>(BrowserCache_GetStaticData);
                BrowserCache.RegisterBrowser(b);
            }
        }

        public static void BrowserCache_GetStaticData(object sender, GetBrowserDataEventArgs e)
        {
            if (e.Browser == LngBrowserCode)
            {
                DataTable dt = new DataTable(LngBrowserCode);
                dt.Columns.Add("CODE");
                dt.Columns.Add("NAME");
                foreach (KeyValuePair<string, ProgNetLanguage> kvp in Languages)
                {
                    dt.Rows.Add(kvp.Key, kvp.Value.Name);
                }
                e.Table = dt;
            }
        }

        #region static fields
        private static string _FullLog = "";
        private static string _NoEntryString = "NO DATA";
        private static Dictionary<string, ProgNetLanguage> Languages = new Dictionary<string, ProgNetLanguage>(StringComparer.OrdinalIgnoreCase);
        private static ProgNetLanguage _Current;
        #endregion

        #region static Properties
        public static string NoEntryString
        {
            get { return _NoEntryString; }
            set { _NoEntryString = value; }
        }
        public static ProgNetLanguage Current
        {
            get { return _Current; }
        }
        public static string CurrentLanguageCode
        {
            get
            {
                if (Current != null)
                    return Current.Code;
                return "";
            }
            set
            {
                try
                {
                    if (Languages.ContainsKey(value))
                    {
                        _Current = Languages[value];
                        OnLanguageChanged();
                    }
                    else
                    {
                        CultureInfo c = new CultureInfo(value);
                        if (c != null)
                        {
                            if (c.ThreeLetterWindowsLanguageName != CurrentLanguageCode)
                            {
                                if (Languages.ContainsKey(c.ThreeLetterWindowsLanguageName))
                                {
                                    _Current = Languages[c.ThreeLetterWindowsLanguageName];
                                    OnLanguageChanged();
                                }
                            }
                        }
                    }
                }
                catch { }
            }
        }
        public static bool LogMissingValues
        {
            get;
            set;
        }
        private static string _LogFile;
        public static string LogFile
        {
            get
            {
                if (_LogFile.IsNull())
                {
                    _LogFile = System.Windows.Forms.Application.StartupPath + "\\Language.log";
                    _FullLog = _LogFile.ReadFile();
                }
                return _LogFile;
            }
            set
            {
                try
                {
                    FileInfo f = new FileInfo(value);
                    _LogFile = value;
                    _FullLog = value.ReadFile();
                }
                catch { }
            }
        }
        #endregion

        #region Static event
        public static event EventHandler LanguageChanged;
        private static void OnLanguageChanged()
        {
            if (LanguageChanged != null)
                LanguageChanged(Current, EventArgs.Empty);
        }
        #endregion

        public static string sGetEnumString(Enum value)
        {
            string str = sGetString(value.GetType().FullName + "." + value.ToString());
            if (str == NoEntryString)
                str = value.ToString();
            return str;
        }
        public static string sGetStringFromMgr(string mgr, string code, params object[] args)
        {
            if (Languages.Count > 0)
            {
                if (Current != null)
                    return Current.GetStringFromMgr(mgr, code, args);
                else
                {
                    LogMissing(mgr, code);
                    return NoEntryString;
                }
            }
            else
            {
                if ("".IsAppRunning())
                {
                    LogMissing(mgr, code);
                }
                return "[" + code + "]";
            }
        }
        public static string sGetString(string code, params object[] args)
        {
            return sGetStringFromMgr("", code, args);
        }


        public static string sGetEnumStringDefault(Enum value, string Default)
        {
            string str = sGetString(value.GetType().FullName + "." + value.ToString());
            if (str == NoEntryString)
                str = Default;
            return str;
        }
        public static string sGetStringFromMgrDefault(string mgr, string code, string Default, params object[] args)
        {
            if (Languages.Count > 0)
            {
                if (Current != null)
                    return Current.GetStringFromMgr(mgr, code, args);
                else
                {
                    LogMissing(mgr, code, Default);
                    return Default;
                }
            }
            else
            {
                if ("".IsAppRunning())
                {
                    LogMissing(mgr, code, Default);
                }
                return "[" + code + "]";
            }
        }

        public static string sGetStringDefault(string code, string Default, params object[] args)
        {
            return sGetStringFromMgrDefault("", code, Default, args);
        }

        public static void SetCurrentLanguage(string code)
        {
            if (Languages.ContainsKey(code))
            {
                _Current = Languages[code];
                OnLanguageChanged();
            }
        }
        private static ProgNetLanguage GetLanguage(string culture)
        {
            ProgNetLanguage lng = null;
            if (culture.IsNotNull())
            {
                CultureInfo c = new CultureInfo(culture);
                if (c != null)
                {
                    if (Languages.ContainsKey(c.ThreeLetterWindowsLanguageName))
                        lng = Languages[c.ThreeLetterWindowsLanguageName];
                    else
                        lng = new ProgNetLanguage(c);
                }
            }
            return lng;
        }
        public static void LoadLanguage(Assembly assembly)
        {
            try
            {
                string name = "";
                string culture = "";
                string[] names = assembly.GetManifestResourceNames();
                foreach (string n in names)
                {
                    culture = n.GetFirstMatch(@"(\w{2}-\w{2})");
                    name = n.GetFirstMatch(@"(.*)(\w{2}-\w{2})").TrimEnd('_');
                    ProgNetLanguage lng = GetLanguage(culture);
                    if (lng != null)
                    {
                        lng.RegisterManager(name, n, assembly);
                    }
                }
            }
            catch { }
        }
        public static void LoadLanguage(string xmlFile)
        {
            string culture = Path.GetFileNameWithoutExtension(xmlFile).GetFirstMatch(@"(\w{2}-\w{2})");
            string name = Path.GetFileNameWithoutExtension(xmlFile).GetFirstMatch(@"(.*)(\w{2}-\w{2})").TrimEnd('_');
            XmlTextReader reader = new XmlTextReader(xmlFile);
            LoadLanguageFromXmlReader(culture, name, reader);
        }
        private static void LoadLanguageFromXmlReader(string culture, string name, XmlTextReader reader)
        {
            ProgNetLanguage lng = GetLanguage(culture);
            if (lng != null)
            {
                Dictionary<string, string> dic = new Dictionary<string, string>();
                while (reader.Read())
                {
                    if (reader.NodeType == XmlNodeType.Element &&
                        reader.Name == "data")
                    {

                    }
                }
                lng.RegisterManager(name, dic);
            }
        }

        public ProgNetLanguage(CultureInfo culture)
        {
            if (culture != null)
            {
                if (!Languages.ContainsKey(culture.ThreeLetterWindowsLanguageName))
                {
                    _Culture = culture;
                    Languages.Add(culture.ThreeLetterWindowsLanguageName, this);
                }
                else
                    throw new ArgumentException("Language with code " + culture.ThreeLetterWindowsLanguageName + " is already added.");
            }
            else
                throw new ArgumentNullException("culture");
        }

        Dictionary<string, ResourceManager> mgrs = new Dictionary<string, ResourceManager>(StringComparer.OrdinalIgnoreCase);
        Dictionary<string, Dictionary<string, string>> dicts = new Dictionary<string, Dictionary<string, string>>(StringComparer.OrdinalIgnoreCase);

        private CultureInfo _Culture;
        public CultureInfo Culture
        {
            get { return _Culture; }
        }

        public string Code
        {
            get
            {
                return _Culture.ThreeLetterWindowsLanguageName;
            }
        }
        public string Name
        {
            get
            {
                return _Culture.NativeName;
            }
        }

        private string GetUniqueName()
        {
            int i = 1;
            string name = "mgr" + i;
            while (mgrs.ContainsKey(name) || dicts.ContainsKey(name))
            {
                i++;
                name = "mgr" + i;
            }
            return name;
        }

        public void RegisterManager(string name, Type type)
        {
            if (name.IsNull() || mgrs.ContainsKey(name))
                name = GetUniqueName();
            PropertyInfo pi =type.GetProperty("ResourceManager", BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public);
            ResourceManager mgr = null;
            if (pi != null)
            {
                mgr = pi.GetValue(null, null) as ResourceManager;
            }
            if (mgr == null)
                mgr = new ResourceManager(type);
            mgr.IgnoreCase = true;
            mgrs.Add(name, mgr);
        }
        public void RegisterManager(string name, string baseName, Assembly assembly)
        {
            if (name.IsNull() || mgrs.ContainsKey(name))
                name = GetUniqueName();
            if (baseName.AsString().EndsWith(".resources"))
                baseName = baseName.XReplace("\\.resources$", "");
            ResourceManager m = new ResourceManager(baseName, assembly);
            m.IgnoreCase = true;
            mgrs.Add(name, m);
        }
        public void RegisterManager(string name, Dictionary<string, string> dic)
        {
            if (name.IsNull() || dicts.ContainsKey(name))
                name = GetUniqueName();
            dicts.Add(name, dic);
        }

        public string GetStringFromMgr(string mgr, string code, params object[] args)
        {
            if (code.IsNull())
                return string.Empty;
            string str = string.Empty;
            if (mgr.IsNotNull() && mgrs.ContainsKey(mgr))
            {
                try
                {
                    str = mgrs[mgr].GetString(code);
                }
                catch { }
            }
            if (str.IsNull())
            {
                if (mgr.IsNotNull() && dicts.ContainsKey(mgr))
                {
                    if (dicts[mgr].ContainsKey(code))
                        str = dicts[mgr][code];
                }
            }
            if (str.IsNull())
            {
                foreach (ResourceManager m in mgrs.Values)
                {
                    try
                    {
                        str = m.GetString(code);
                        if (str.IsNotNull())
                            break;
                    }
                    catch { }

                }

            }
            if (str.IsNull())
            {
                foreach (Dictionary<string, string> d in dicts.Values)
                {
                    if (d.ContainsKey(code))
                    {
                        str = d[code];
                        break;
                    }
                }
            }
            if (str.IsNotNull())
            {
                str = str.XFormat(args);
            }
            if (str.IsNull())
                str = NoEntryString;
            if (str == NoEntryString)
            {
                LogMissing(mgr, code);
            }
            return str;
        }

        public string GetString(string code, params object[] args)
        {
            return GetStringFromMgr("", code, args);
        }

        private static void LogMissing(string mgr, string code, string def)
        {
            if (LogMissingValues)
            {
                string mgrStr = "Manager = [" + mgr + "]\n";
                string str = "\t<data name=\"" + code + "\" xml:space=\"preserve\">\n" +
                             "\t\t<value>" + def + "</value>\n" +
                             "\t</data>";
                if (_FullLog.IsNull())
                    _FullLog = LogFile.ReadFile();
                if (!_FullLog.AsString().Contains(str))
                {
                    int idx = _FullLog.AsString().IndexOf(mgrStr);
                    if (idx == -1)
                    {
                        _FullLog = _FullLog.AppendLine(mgrStr);
                        idx = _FullLog.Length;
                    }
                    else
                    {
                        idx += mgrStr.Length;
                    }
                    _FullLog = _FullLog.AsString().Insert(idx, str + "\n");
                    _FullLog.WriteToFile(LogFile);
                }
            }
        }
        private static void LogMissing(string mgr, string code)
        {
            LogMissing(mgr, code, "");
        }

    }
}
